use std::{fmt, any::Any};
use std::sync::Arc;
use crate::index::error::IndexError;
use crate::recipe::VersionFrom;
use crate::{recipe::{RecipeError, VersionError}, container::ContainerError};

pub type RestoreResult<T> = Result<T,RestoreError>;

#[derive(Debug)]
pub enum RestoreError {
    DirCreate(String,std::io::Error),
    FileCreate(String,std::io::Error),
    FileWrite(std::io::Error),
    Container(ContainerError),
    VersionMissing(Option<VersionFrom>),
    BaseChunkNotFound,
    DoubleDelta,
    Decode,
    UnexpectedReChunkError(Box<dyn Any + Send>),
    Recipe(RecipeError),
    Version(VersionError),
    Index(IndexError),
    Collect(RestoreErrorCollector),
}

impl fmt::Display for RestoreError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RestoreError::DirCreate(path,e) => write!(f,"Error restore directory path {} can't be created:{}",path,e),
            RestoreError::FileCreate(path,e) => write!(f,"Error restore file path {} can't be created:{}",path,e),
            RestoreError::UnexpectedReChunkError(_) => write!(f,"Error unexpected rechunk error"),
            RestoreError::VersionMissing(v) => 
                                        write!(f,"Error {} version missing",v.as_ref().map_or("default".to_string(), |v|format!("{:?}",v))),
            RestoreError::FileWrite(e) => write!(f,"Error write to file error:{}",e),
            RestoreError::BaseChunkNotFound => write!(f,"Error base chunk of delta chunk not found"),
            RestoreError::DoubleDelta => write!(f,"Error base chunk of delta chunk is also a delta chunk"),
            RestoreError::Decode => write!(f,"Error decode error"),
            RestoreError::Container(e) => write!(f,"container::{}",e),
            RestoreError::Recipe(e) => write!(f,"recipe::Error {}",e),
            RestoreError::Index(e) => write!(f,"index::{}",e),
            RestoreError::Collect(e) => write!(f,"{}",e),
            RestoreError::Version(e) => write!(f,"version::{}",e),
        }
    }
}

impl std::error::Error for RestoreError { }

impl From<ContainerError> for RestoreError {
    fn from(value: ContainerError) -> Self {
        Self::Container(value)
    }
}

impl From<RecipeError> for RestoreError {
    fn from(value: RecipeError) -> Self {
        Self::Recipe(value)
    }
}

impl From<VersionError> for RestoreError {
    fn from(value: VersionError) -> Self {
        Self::Version(value)
    }
}

impl From<IndexError> for RestoreError {
    fn from(value: IndexError) -> Self {
        Self::Index(value)
    }
}

impl From<RestoreErrorCollector> for RestoreError {
    fn from(value: RestoreErrorCollector) -> Self {
        Self::Collect(value)
    }
}

impl From<Box<dyn Any + Send>> for RestoreError {
    fn from(value: Box<dyn Any + Send>) -> Self {
        Self::UnexpectedReChunkError(value)
    }
}


#[derive(Debug,Clone)]
pub struct RestoreErrorCollector {
    errs:Arc<parking_lot::Mutex<Vec<RestoreError>>>,
}

impl RestoreErrorCollector {
    pub fn new() -> Self {
        Self { errs:Arc::new(parking_lot::Mutex::new(Vec::new())) }
    }

    pub fn collect<T>(&self,t:RestoreResult<T>) {
        if let Err(e) = t {self.errs.lock().push(e);}
    }

    pub fn try_return<T>(self,t:T) -> RestoreResult<T> {
        let result = self.errs.lock().is_empty();
        if result {
            Ok(t)
        } else {
            Err(self.into())
        }
    }
}

impl fmt::Display for RestoreErrorCollector {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let errs = self.errs.lock();
        if errs.len() == 1 { return write!(f,"{}",errs[0]); }
        let mut info = "".to_string();
        for err in errs.iter() {
            info += format!("\n\t{}",err).as_str();
        }
        write!(f,"{}",info)
    }
}


